﻿/*****************************************
 * This code is mostly written by peter  *
 * Necessary adjustment for Sims3Package *
 * Library is made.                      *
 * ***************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Sims3Package.Wrapper
{
    /// <summary>
    /// Wrapper for STBL resource
    /// </summary>
    public class STBLResourceWrapper : Wrapper 
    {
        public new static List<UInt32> ResourceTypes { get { return new List<uint>(mResourceTypes); } }
        private new static UInt32[] mResourceTypes = { 0x220557DAU };

        ushort unknown1;
        ushort unknown2;
        uint unknown3;
        [WrapperVisible(true)]
        public Dictionary<ulong, string> StringEntries { get; set; }
        /// <summary>
        /// Standard constructor
        /// </summary>
        /// <param name="s">Stream that contained in entry, null to represent new resource</param>
        public STBLResourceWrapper(Stream s)
            : base(s)
        {
            stream = s; Parse();
        }

        private void Parse()
        {
            if (stream == null || stream.Length < 1) { this.StringEntries = new Dictionary<ulong, string>(); return; }
            stream.Position = 0;
            BinaryReader r = new BinaryReader(stream);

            uint magic = r.ReadUInt32();
            if (magic != FOURCC("STBL"))
                throw new InvalidDataException(String.Format("Expected magic tag 0x{0:X8}; read 0x{1:X8}; position 0x{2:X8}",
                    FOURCC("STBL"), magic, stream.Position));
            byte version = r.ReadByte();
            if (version != 0x02)
                throw new InvalidDataException(String.Format("Expected version 0x02; read 0x{0:X2}; position 0x{1:X8}",
                    version, stream.Position));

            unknown1 = r.ReadUInt16();

            uint count = r.ReadUInt32();

            unknown2 = r.ReadUInt16();
            unknown3 = r.ReadUInt32();

            StringEntries = new Dictionary<ulong, string>();
            for (int i = 0; i < count; i++)
            {
                ulong key = r.ReadUInt64();
                string value = System.Text.Encoding.Unicode.GetString(r.ReadBytes(r.ReadInt32() * 2));
                if (StringEntries.ContainsKey(key)) continue; // Patch 1.6 has problems in the STBLs (World Adventures sneaked into the DeltaBuild0 file)
                StringEntries.Add(key, value);
            }
        }
        /// <summary>
        /// Returns the stream written into entry
        /// </summary>
        /// <returns>Stream.IO.Stream</returns>
        public override Stream UnParse()
        {
            MemoryStream ms = new MemoryStream();

            BinaryWriter w = new BinaryWriter(ms);

            w.Write((uint)FOURCC("STBL"));
            w.Write((byte)0x02);

            w.Write(unknown1);

            if (StringEntries == null) StringEntries = new Dictionary<ulong, string>();
            w.Write(StringEntries.Count);

            w.Write(unknown2);
            w.Write(unknown3);

            foreach (var kvp in StringEntries)
            {
                w.Write(kvp.Key);
                w.Write(kvp.Value.Length);
                w.Write(System.Text.Encoding.Unicode.GetBytes(kvp.Value));
            }
            ms.Position = 0;
            return ms;
        }
        /// <summary>
        /// Used in text preview
        /// </summary>
        public override string Value
        {
            get
            {
                if (this.StringEntries == null) return string.Empty;
                StringBuilder b = new StringBuilder();
                long count = 0;
                foreach (var kvp in this.StringEntries)
                {
                    b.Append(string.Format("[{0:X}]  ", count++)).Append(String.Format("0x{0}: ", kvp.Key.ToString("X").PadLeft(16, '0'))).Append(kvp.Value).AppendLine();
                }
                return b.ToString();
            }
        }

        /// <summary>
        /// Convert a string (up to 8 characters) to a UInt64
        /// </summary>
        /// <param name="s">String to convert</param>
        /// <returns>UInt64 packed representation of <paramref name="s"/></returns>
        private static UInt64 FOURCC(string s)
        {
            if (s.Length > 8) throw new InvalidDataException("Length is not correct");
            UInt64 i = 0;
            for (int j = s.Length - 1; j >= 0; j--) i += ((uint)s[j]) << (j * 8);
            return i;
        }

        /// <summary>
        /// Convert a UInt64 to a string (up to 8 characters, high-order zeros omitted)
        /// </summary>
        /// <param name="i">Bytes to convert</param>
        /// <returns>String representation of <paramref name="i"/></returns>
        private static string FOURCC(UInt64 i)
        {
            string s = "";
            for (int j = 7; j >= 0; j--) { char c = (char)((i >> (j * 8)) & 0xff); if (s.Length > 0 || c != 0) s = c + s; }
            return s;
        }
    }
}
